package com.yufei.utils;
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class LinkNormalizer {
    private static Log log = LogFactory.getLog(LinkNormalizer.class);
    static final Map<Pattern, String> ESCAPE_PATTERNS = new HashMap<Pattern, String>();

    static {
        ESCAPE_PATTERNS.put(Pattern.compile("\\s"), "%20");
        ESCAPE_PATTERNS.put(Pattern.compile("\\^"), "%5E");
        ESCAPE_PATTERNS.put(Pattern.compile("\""), "%22");
        ESCAPE_PATTERNS.put(Pattern.compile("'"), "%27");
        ESCAPE_PATTERNS.put(Pattern.compile("`"), "%60");
        ESCAPE_PATTERNS.put(Pattern.compile("\\["), "%5B");
        ESCAPE_PATTERNS.put(Pattern.compile("\\]"), "%5D");
        ESCAPE_PATTERNS.put(Pattern.compile("\\{"), "%7B");
        ESCAPE_PATTERNS.put(Pattern.compile("\\}"), "%7D");
    }

    static final Map<Pattern, String> UNESCAPE_PATTERNS = new HashMap<Pattern, String>();

    static {
        UNESCAPE_PATTERNS.put(Pattern.compile("%20"), " ");
        UNESCAPE_PATTERNS.put(Pattern.compile("%5E"), "^");
        UNESCAPE_PATTERNS.put(Pattern.compile("%22"), "\"");
        UNESCAPE_PATTERNS.put(Pattern.compile("%27"), "'");
        UNESCAPE_PATTERNS.put(Pattern.compile("%60"), "`");
        UNESCAPE_PATTERNS.put(Pattern.compile("%5B"), "[");
        UNESCAPE_PATTERNS.put(Pattern.compile("%5D"), "]");
        UNESCAPE_PATTERNS.put(Pattern.compile("%7B"), "{");
        UNESCAPE_PATTERNS.put(Pattern.compile("%7D"), "}");
    }


    static protected final Map<Pattern, String> URL_REPLACEMENTS = new HashMap<Pattern, String>();

    static {
        /**
         * Replace tab, line feed, and carriage-return characters.
         */
        URL_REPLACEMENTS.put(Pattern.compile("[\t\n\r]"), "");

    }

    static protected final Map<Pattern, String> PATH_REPLACEMENTS = new HashMap<Pattern, String>();

    static {
        /**
         * Replace any "\" with "/"
         */
        PATH_REPLACEMENTS.put(Pattern.compile("\\\\"), "/");


        /**
         * Replace "/path/../index.html" with "/index.html"
         */
        PATH_REPLACEMENTS.put(Pattern.compile("/[^/]*/\\.\\./"), "/");

        /**
         * For URL starts with "/", replace "/../index.html" with "/index.html"
         */
        PATH_REPLACEMENTS.put(Pattern.compile("^/([\\.]{1,2}/)*"), "/");

        /**
         * Replace any "//" or "/./" with "/"
         */
        PATH_REPLACEMENTS.put(Pattern.compile("/([\\.]?/)*"), "/");

    }

    /**
     * Support full URL (e.g. http://www.apache.org/style/style.css) and partial URI (e.g. /style/style.css)
     */
    public String normalize(String url) {
        if (url == null) throw new IllegalArgumentException("url is null");
        url = url.trim();
        for (Pattern pattern : URL_REPLACEMENTS.keySet()) {
            url = pattern.matcher(url).replaceAll(URL_REPLACEMENTS.get(pattern));
        }

        int queryStringIndex = url.indexOf('?');
        int pageAnchorIndex = url.indexOf('#');
        String hostTest = queryStringIndex != -1 ? url.substring(0, queryStringIndex) : url;
        int hostIndex = hostTest.indexOf("://") != -1 ? hostTest.indexOf("://") + 3 : -1;
        int pathIndex = hostIndex != -1 ? url.indexOf('/', hostIndex) : 0; //schema must present if use host

        if(pathIndex > queryStringIndex && queryStringIndex > 0) {
            pathIndex = -1;
        }
        int pathEndIndex = StrictMath.min(pageAnchorIndex != -1 ? pageAnchorIndex : url.length(), queryStringIndex != -1 ? queryStringIndex : url.length());

        String schemaHostPort = pathIndex != -1 ? url.substring(0, pathIndex) : url.substring(0, url.length());
        String path = pathIndex != -1 ? url.substring(pathIndex, pathEndIndex) : "";
        String suffix = pathEndIndex != url.length() ? url.substring(pathEndIndex, url.length()) : "";

        //lowercase & trim port
        schemaHostPort = schemaHostPort.toLowerCase();
        if (schemaHostPort.startsWith("http:") && schemaHostPort.endsWith(":80"))
            schemaHostPort = schemaHostPort.substring(0, schemaHostPort.length() - ":80".length());
        if (schemaHostPort.startsWith("https:") && schemaHostPort.endsWith(":443"))
            schemaHostPort = schemaHostPort.substring(0, schemaHostPort.length() - ":443".length());

        //apply pattens
        if (path != null && !"".equals(path))
            for (Pattern pattern : PATH_REPLACEMENTS.keySet()) {
                path = pattern.matcher(path).replaceAll(PATH_REPLACEMENTS.get(pattern));
            }
        else {
            path = "/";
        }

        String normalized = schemaHostPort + path + suffix;
        if (log.isTraceEnabled())
            log.trace("normalize() - normalized: " + normalized + ", schemaHostPort: " + schemaHostPort + ", path: " + path + ", suffix: " + suffix + ", original url: " + url);
        return normalized;
    }

    public String escape(String url) {
        String temp;
        for (Pattern pattern : ESCAPE_PATTERNS.keySet()) {
            url = pattern.matcher(url).replaceAll(ESCAPE_PATTERNS.get(pattern));
        }
        return url;
    }

    public String unescape(String url) {
        String temp;
        for (Pattern pattern : UNESCAPE_PATTERNS.keySet()) {
            url = pattern.matcher(url).replaceAll(UNESCAPE_PATTERNS.get(pattern));
        }
        return url;
    }


}
